# --*-- coding:utf-8 --*--
# @Time : 2025/6/20 17:05
# @Author : Xu
# @Email : wsxzy98@outlook.com
# @File : stock_quant.py
# @Software : VScode

from odoo import models, fields, api
import logging
_logger = logging.getLogger(__name__)

try:
    from psycopg2 import Error
except Exception:
    Error = Exception

def to_id(val):
    """辅助：把传入值转成int id或False，兼容recordset/int/None"""
    if hasattr(val, 'id'):
        return val.id
    elif isinstance(val, (list, tuple)) and val and hasattr(val[0], 'id'):
        return val[0].id
    return val or False

class StockQuant(models.Model):
    _inherit = 'stock.quant'

    origin_name = fields.Char('原产地')  # 新增字段
    pack_datetime = fields.Datetime('打包时间')  # 新增字段
    customer_reference = fields.Char('客户参考号')  # 新增字段

    new_serial_number = fields.Char(
        '新编码',
        compute='_compute_new_serial_number'
    )# 新增字段

    @api.model
    def action_sync_customer_reference(self, package_id):
        """
        主动触发同步本包裹所有 quant 的客户参考号。
        不做任何返回，只做后台数据修正。
        """
        package = self.env['stock.quant.package'].browse(package_id)
        # 只调用 stock_package.py 已有的方法
        if package.exists():
            package.action_sync_customer_reference()
    
    @api.model
    def _get_inventory_fields_write(self):
        fields = super()._get_inventory_fields_write()
        if 'customer_reference' not in fields:
            fields.append('customer_reference')
        return fields

    @api.model
    def _get_inventory_fields_create(self):
        """
        唯一性分组扩展，加入 customer_reference
        这样系统任何包裹动作，只要客户参考号不同，quant都会分行，不会合并
        """
        _logger.warning('[get_inventory_fields_create] 自定义分组生效！')
        fields = super()._get_inventory_fields_create()
        if 'customer_reference' not in fields:
            fields.append('customer_reference')
        return fields

    @api.model
    def _merge_quants(self):
        """
        Deduplicate quants, BUT keep customer_reference as a grouping key so
        quants with different customer_reference will NOT be merged.
        """
        params = []
        # ---- 只在 GROUP BY 里新增 COALESCE(customer_reference,'') ----
        query = """WITH
                        dupes AS (
                            SELECT
                                min(id) as to_update_quant_id,
                                (array_agg(id ORDER BY id))[2:array_length(array_agg(id), 1)] as to_delete_quant_ids,
                                GREATEST(0, SUM(reserved_quantity)) as reserved_quantity,
                                SUM(inventory_quantity) as inventory_quantity,
                                SUM(quantity) as quantity,
                                MIN(in_date) as in_date
                            FROM stock_quant
        """
        if self._ids:
            query += """
                            WHERE
                                location_id in %s
                                AND product_id in %s
            """
            params = [tuple(self.location_id.ids), tuple(self.product_id.ids)]

        # 关键改动：GROUP BY 多了 COALESCE(customer_reference, '')
        query += """
                            GROUP BY product_id, company_id, location_id, lot_id, package_id, owner_id,
                                     COALESCE(customer_reference, '')
                            HAVING count(id) > 1
                        ),
                        _up AS (
                            UPDATE stock_quant q
                               SET quantity = d.quantity,
                                   reserved_quantity = d.reserved_quantity,
                                   inventory_quantity = d.inventory_quantity,
                                   in_date = d.in_date
                              FROM dupes d
                             WHERE d.to_update_quant_id = q.id
                        )
                   DELETE FROM stock_quant
                    WHERE id in (SELECT unnest(to_delete_quant_ids) FROM dupes)
        """
        try:
            with self.env.cr.savepoint():
                self.env.cr.execute(query, params)
                self.env.invalidate_all()
        except Error as e:
            _logger.info('an error occurred while merging quants: %s', getattr(e, 'pgerror', e))

    @api.model
    def _gather(self, product_id, location_id, lot_id=None, package_id=None, owner_id=None, strict=False, customer_reference=None, **kwargs):
        domain = [
            ('product_id', '=', to_id(product_id)),
            ('location_id', '=', to_id(location_id)),
            ('lot_id', '=', to_id(lot_id)),
            ('package_id', '=', to_id(package_id)),
            ('owner_id', '=', to_id(owner_id)),
            ('customer_reference', '=', customer_reference or False),
        ]
        _logger.warning(f'[quant.gather] product_id={product_id}, location_id={location_id}, lot_id={lot_id}, package_id={package_id}, owner_id={owner_id}, customer_reference={customer_reference}, domain={domain}')
        quants = self.search(domain)
        return quants

    @api.model
    def create(self, vals):
        # 自动赋值打包时间
        if vals.get('package_id') and not vals.get('pack_datetime'):
            vals['pack_datetime'] = fields.Datetime.now()
        
        # 自动赋值原产地
        prod_id = to_id(vals.get('product_id'))
        if not vals.get('origin_name') and prod_id:
            prod = self.env['product.product'].browse(prod_id)
            if prod and prod.product_tmpl_id.origin:
                vals['origin_name'] = prod.product_tmpl_id.origin

        # 用to_id确保安全
        pkg_id = to_id(vals.get('package_id'))
        prod_id = to_id(vals.get('product_id'))
        qty = vals.get('quantity')

        # 仅产品和数量能唯一定位，按数量和产品分配客户参考号
        if not vals.get('customer_reference') and pkg_id and prod_id is not False and qty is not None:
            ml_domain = [
                ('result_package_id', '=', pkg_id),
                ('product_id', '=', prod_id),
                ('qty_done', '=', qty),  # 只用产品和数量匹配
            ]
            move_lines = self.env['stock.move.line'].search(ml_domain, order='id')
            customer_refs = [ml.customer_reference for ml in move_lines if ml.customer_reference]

            # 查询当前包裹里已经生成的 quant，有哪些 customer_reference 被用过
            q_domain = [
                ('package_id', '=', pkg_id),
                ('product_id', '=', prod_id),
                ('quantity', '=', qty),
            ]
            existing_quants = self.env['stock.quant'].search(q_domain)
            used_refs = [q.customer_reference for q in existing_quants if q.customer_reference]

            # 分配未用过的 customer_reference
            assign_ref = next((r for r in customer_refs if r and r not in used_refs), None)
            if assign_ref:
                vals['customer_reference'] = assign_ref
            # 否则保持不写，不要写 ''

        # 同时：若上游通过 context 传了 customer_reference，就直接用它
        if not vals.get('customer_reference'):
            ctx_ref = self.env.context.get('customer_reference')
            if ctx_ref:
                vals['customer_reference'] = ctx_ref
                
        quant = super().create(vals)
        # 只要创建时分配了包裹，自动同步（安全调用，不递归）
        pkg_id = vals.get('package_id')
        if pkg_id:
            quant.action_sync_customer_reference(pkg_id)
        return quant
    
    @api.onchange('product_id')
    def _onchange_product_id_origin_name(self):
        # 产品变化自动同步origin_name
        if self.product_id and not self.origin_name:
            self.origin_name = self.product_id.product_tmpl_id.origin

    def write(self, vals):
        # 包裹赋值打包时间
        if vals.get('package_id') and not vals.get('pack_datetime'):
            vals['pack_datetime'] = fields.Datetime.now()
        return super().write(vals)
    
    @api.depends('product_id')
    def _compute_new_serial_number(self):
        for quant in self:
            tmpl = quant.product_id.product_tmpl_id
            quant.new_serial_number = tmpl.new_variable or '' if tmpl else ''
